using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input.Touch;

namespace Perovich.GameObjects.Helpers
{
    /// <summary>
    /// A singelton class that encapsulates collecting and responging to Windows Phone Gestures.
    /// </summary>
    /// <remarks>Initialize must be called before using the Instance field.</remarks>
    public sealed class GestureHelper : Component
    {
        private static GestureHelper instance;

        /// <summary>
        /// Gets the currently in use instance of the <see cref="GestureHelper"/> class.
        /// </summary>
        public static GestureHelper Instance {
            get
            {
                if (instance == null)
                    throw new InvalidOperationException("Initialize must be called before accessing the Instance property.");
                return instance;
            }
            private set
            {
                instance = value;
            }
        }

        /// <summary>
        /// Initializes the <see cref="GestureHelper"/> using the specified <see cref="Perovich.GameObjects.Interfaces.ICanHoldComponents"/>.
        /// </summary>
        /// <param name="game">The <see cref="Perovich.GameObjects.Interfaces.ICanHoldComponents"/> instance to use.</param>
        public static void Initialize(Interfaces.ICanHoldComponents game)
        {
            if (instance == null)
            {
                GameObject obj;
                while ((obj = game as GameObject) != null)
                    game = obj.Parent;
                Instance = new GestureHelper(game);
                game.Components.Add(Instance);
                Perovich.GameObjects.ComponentsExtensions.Helpers.Add(new Helper(Instance.GetType().GetMethod("Reset", System.Reflection.BindingFlags.Static)));
            }
        }

        /// <summary>
        /// Resets the <see cref="GestureHelper"/>. For use after the <see cref="Perovich.GameObjects.Interfaces.ICanHoldComponents"/> is no longer in use.
        /// </summary>
        public static void Reset()
        {
            Instance = null;
        }

        private Dictionary<GestureType, List<Action<GestureSample>>> gestureCallbacks;

        private GestureHelper(Interfaces.ICanHoldComponents game)
            : base(game)
        {
            TouchPanel.EnabledGestures = GestureType.None;
            gestureCallbacks = new Dictionary<GestureType, List<Action<GestureSample>>>();
        }

        /// <summary>
        /// Adds a callback function for the given <see cref="GestureType"/>.
        /// </summary>
        /// <param name="gestureType">The <see cref="GestureType"/> to respond to.</param>
        /// <param name="callback">The callback to run.</param>
        public static void AddCallback(GestureType gestureType, Action<GestureSample> callback)
        {
            Instance.AddCallbackInternal(gestureType, callback);
        }
        private void AddCallbackInternal(GestureType gestureType, Action<GestureSample> callback)
        {
            if (!gestureCallbacks.ContainsKey(gestureType))
            {
                TouchPanel.EnabledGestures |= gestureType;
                gestureCallbacks.Add(gestureType, new List<Action<GestureSample>>());
            }

            gestureCallbacks[gestureType].Add(callback);
        }

        /// <summary>
        /// Clears all gesture callbacks in the <see cref="GestureHelper"/>.
        /// </summary>
        public void Clear()
        {
            TouchPanel.EnabledGestures = GestureType.None;
            gestureCallbacks.Clear();
        }

        /// <summary>
        /// Clears all gesture callbacks in the <see cref="GestureHelper"/> for the given <see cref="GestureType"/>.
        /// </summary>
        /// <param name="gestureType">The <see cref="GestureType"/> to clear.</param>
        public void ClearGesture(GestureType gestureType)
        {
            TouchPanel.EnabledGestures -= gestureType;
            gestureCallbacks.Remove(gestureType);
        }


        /// <summary>
        /// Updates the gesture helper
        /// </summary>
        /// <param name="gameTime"></param>
        public override void Update(GameTime gameTime)
        {
            while (TouchPanel.IsGestureAvailable)
            {
                GestureSample gestureSample = TouchPanel.ReadGesture();

                if (gestureCallbacks.ContainsKey(gestureSample.GestureType))
                {
                    foreach (Action<GestureSample> callback in gestureCallbacks[gestureSample.GestureType])
                        callback(gestureSample);
                }
            }

            base.Update(gameTime);
        }
    }
}
